001    package net.sf.xdc.util;
002    
003    /*
004     *  Copyright 2005-2006 Jens Voß.
005     *
006     *  Licensed under the GNU Lesser General Public License (the "License");
007     *  you may not use this file except in compliance with the License.
008     *  You may obtain a copy of the License at
009     *
010     *       http://opensource.org/licenses/lgpl-license.php
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    import java.io.FileNotFoundException;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.Reader;
023    import java.io.StringWriter;
024    import java.net.MalformedURLException;
025    import java.net.URL;
026    import java.nio.charset.Charset;
027    import java.util.Enumeration;
028    import java.util.HashMap;
029    import java.util.Iterator;
030    import java.util.Map;
031    import java.util.Properties;
032    import javax.xml.transform.Result;
033    import javax.xml.transform.Source;
034    import javax.xml.transform.Transformer;
035    import javax.xml.transform.TransformerException;
036    import javax.xml.transform.TransformerFactory;
037    import javax.xml.transform.dom.DOMResult;
038    import javax.xml.transform.dom.DOMSource;
039    import javax.xml.transform.stream.StreamResult;
040    import javax.xml.transform.stream.StreamSource;
041    
042    import org.apache.log4j.Logger;
043    import org.apache.xalan.processor.TransformerFactoryImpl;
044    import org.apache.xalan.transformer.TransformerImpl;
045    import org.w3c.dom.Document;
046    import org.w3c.dom.Element;
047    import org.xml.sax.ContentHandler;
048    import org.xml.sax.helpers.LocatorImpl;
049    
050    /**
051     * This is a utility class containg various method related to XSLT
052     * transformations.
053     *
054     * @author Jens Voß
055     * @since 0.5
056     * @version 0.5
057     */
058    public class XslUtils {
059    
060      private static final Logger LOG = Logging.getLogger();
061      private static final TransformerFactory FACTORY =
062              TransformerFactory.newInstance();
063    
064      private static final Map XSL_CACHE = new HashMap();
065    
066      static {
067        if (FACTORY instanceof TransformerFactoryImpl) {
068          FACTORY.setAttribute(TransformerFactoryImpl.FEATURE_SOURCE_LOCATION,
069                               Boolean.TRUE);
070        }
071      }
072    
073      private static Element createConfiguration(Map tables) throws XmlException {
074        Document doc = XmlUtils.createDocument();
075        Element configEl = doc.createElement("configuration");
076        for (Iterator tableNames = tables.keySet().iterator(); tableNames.hasNext();) {
077          String tableName = (String) tableNames.next();
078          Properties table = (Properties) tables.get(tableName);
079          Element tableEl = doc.createElement("table");
080          tableEl.setAttribute("name", tableName);
081          for (Enumeration keys = table.propertyNames(); keys.hasMoreElements();) {
082            String key = (String) keys.nextElement();
083            Element entryEl = doc.createElement("entry");
084            tableEl.appendChild(entryEl);
085            Element keyEl = doc.createElement("key");
086            keyEl.appendChild(doc.createTextNode(key));
087            entryEl.appendChild(keyEl);
088            Element valEl = doc.createElement("value");
089            valEl.appendChild(doc.createTextNode(table.getProperty(key)));
090            entryEl.appendChild(valEl);
091          }
092          configEl.appendChild(tableEl);
093        }
094        return configEl;
095      }
096    
097      private static void transform(Source xml, Source xsl, Map tables,
098                                    Result out)
099              throws TransformerException, XmlException {
100        Transformer transformer = FACTORY.newTransformer(xsl);
101        TransformerImpl impl = ((TransformerImpl) transformer);
102        ContentHandler handler = impl.getInputContentHandler();
103        handler.setDocumentLocator(new LocatorImpl());
104        XSL_CACHE.put(xsl.getSystemId(), transformer);
105        transformer.clearParameters();
106        if (tables != null) {
107          transformer.setParameter("configuration", createConfiguration(tables));
108        }
109        transformer.transform(xml, out);
110      }
111    
112      private static void transform(Source xml, Transformer transformer,
113                                    Map tables, Result out)
114              throws TransformerException, XmlException {
115        transformer.clearParameters();
116        if (tables != null) {
117          transformer.setParameter("configuration", createConfiguration(tables));
118        }
119        transformer.transform(xml, out);
120      }
121    
122      private static Document transform(Source xml, Source xsl, Map tables)
123              throws TransformerException, XmlException {
124        Document retVal = XmlUtils.createDocument();
125        transform(xml, xsl, tables, new DOMResult(retVal));
126        return retVal;
127      }
128    
129      private static Document transform(Source xml, Transformer transformer, Map tables)
130              throws TransformerException, XmlException {
131        Document retVal = XmlUtils.createDocument();
132        transform(xml, transformer, tables, new DOMResult(retVal));
133        return retVal;
134      }
135    
136      /**
137       * This method attempts to transform an XML structure contained in a file
138       * using an XSLT stylesheet contained in a resource (which can be loaded
139       * by the <code>Classloader</code> of this class) into a DOM
140       * <code>Document</code>.
141       *
142       * @param xmlFileName The path (in the file system) of the XML input file
143       * @param charset The file encoding charset
144       * @param xslResourceName The name of the XSLT stylesheet resource; either
145       *        the name by which the resource is accessed by the
146       *        <code>Classloader</code> or any valid URL
147       * @param tables Optional key-value pairs passed as stylesheet parameters
148       *        to the transformer
149       * @return An XML document which is the result of the transformation
150       * @throws XmlException
151       */
152      public static Document transform(String xmlFileName, Charset charset,
153                                       String xslResourceName, Map tables)
154              throws XmlException {
155        Reader in = null;
156        try {
157          in = IOUtils.getReader(xmlFileName, charset);
158          StreamSource source = new StreamSource(xmlFileName);
159          source.setSystemId(xmlFileName);
160          return transform(source, xslResourceName, tables);
161        }
162        catch (FileNotFoundException e) {
163          throw new XmlException(e);
164        }
165        finally {
166          try {
167            if (in != null) {
168              in.close();
169            }
170          }
171          catch (IOException e) {
172            e.printStackTrace();
173          }
174        }
175      }
176    
177      /**
178       * This method attempts to transform an XML structure contained in a DOM
179       * <code>Document</code> using an XSLT stylesheet contained in a resource
180       * (which can be loaded by the <code>Classloader</code> of this class)
181       * into a DOM <code>Document</code>.
182       *
183       * @param xml The DOM document used as XML input
184       * @param xslResourceName The name of the XSLT stylesheet resource; either
185       *        the name by which the resource is accessed by the
186       *        <code>Classloader</code> or any valid URL
187       * @param tables Optional key-value pairs passed as stylesheet parameters
188       *        to the transformer
189       * @return An XML document which is the result of the transformation
190       * @throws XmlException
191       */
192      public static Document transform(Document xml, String xslResourceName,
193                                       Map tables)
194              throws XmlException {
195        return transform(new DOMSource(xml), xslResourceName, tables);
196      }
197    
198      private static Document transform(Source xml, String xslResourceName,
199                                        Map tables) throws XmlException {
200        InputStream xsl = null;
201        try {
202          URL xslResource;
203          try {
204            xslResource = new URL(xslResourceName);
205          }
206          catch (MalformedURLException e) {
207            xslResource = new URL("classpath:/" + xslResourceName);
208          }
209          Transformer transformer = (Transformer) XSL_CACHE.get(xslResource.toExternalForm());
210          if (transformer != null) {
211            if (LOG.isDebugEnabled()) {
212              LOG.debug("Using cached transformer for xsl resource "
213                        + xslResourceName);
214            }
215            return transform(xml, transformer, tables);
216          }
217          else {
218            if (LOG.isDebugEnabled()) {
219              LOG.debug(
220                      "Using new transformer for xsl resource " + xslResourceName);
221            }
222            xsl = xslResource.openStream();
223            StreamSource source = new StreamSource(xsl);
224            source.setSystemId(xslResource.toExternalForm());
225            return transform(xml, source, tables);
226          }
227        }
228        catch (MalformedURLException e) {
229          throw new XmlException(e);
230        }
231        catch (TransformerException e) {
232          throw new XmlException(e);
233        }
234        catch (IOException e) {
235          throw new XmlException(e);
236        }
237        finally {
238          if (xsl != null) {
239            try {
240              xsl.close();
241            }
242            catch (IOException e) {
243              throw new XmlException(e);
244            }
245          }
246        }
247      }
248    
249      /**
250       * This method attempts to transform an XML structure contained in a file
251       * using an XSLT stylesheet contained in a resource (which can be loaded
252       * by the <code>Classloader</code> of this class) into a <code>String</code>.
253       *
254       * @param xmlFileName The path (in the file system) of the XML input file
255       * @param xslResourceName The name of the XSLT stylesheet resource; either
256       *        the name by which the resource is accessed by the
257       *        <code>Classloader</code> or any valid URL
258       * @param tables Optional key-value pairs passed as stylesheet parameters
259       *        to the transformer
260       * @return A String containing the XML document which is the result of the
261       *         transformation
262       * @throws XmlException
263       */
264      public static String transformToString(String xmlFileName, String xslResourceName, Map tables)
265              throws XmlException {
266        InputStream xsl = null;
267        try {
268          URL xslResource = new URL("classpath:/" + xslResourceName);
269          xsl = xslResource.openStream();
270          StreamSource source = new StreamSource(xsl);
271          source.setSystemId(xslResource.toExternalForm());
272    
273          StringWriter retVal = new StringWriter();
274          StreamResult result = new StreamResult(retVal);
275          transform(new StreamSource(xmlFileName), source, tables, result);
276          retVal.close();
277          return retVal.getBuffer().toString();
278        }
279        catch (MalformedURLException e) {
280          throw new XmlException(e);
281        }
282        catch (TransformerException e) {
283          throw new XmlException(e);
284        }
285        catch (IOException e) {
286          throw new XmlException(e);
287        }
288        finally {
289          if (xsl != null) {
290            try {
291              xsl.close();
292            }
293            catch (IOException e) {
294              throw new XmlException(e);
295            }
296          }
297        }
298      }
299    
300      private XslUtils() {
301      }
302    
303    }